﻿using AutoMapper;
using Mes.Application.Common.Resource;
using Mes.Application.Common;
using Mes.Application.DTO.Inputs.Quality;
using Mes.Application.DTO.Results.Quality;
using Mes.Domain.BoundedContext;
using Mes.Domain.BoundedContext.Entities.Quality;
using Mes.Infrastructure.Data.BoundedContext.Repositories.Quality;
using System.Linq.Expressions;
using Microsoft.EntityFrameworkCore;
using Mes.Infrastructure.Data.BoundedContext.Extensions;
using Mes.Infrastructure.Data.BoundedContext.Repositories.MasterData;

namespace Mes.Application.Services.Quality
{
    public class QmSpecService : IQmSpecService
    {
        private readonly IMapper _mapper;
        private readonly IQmSpecRepository _qmSpecRepository;
        private readonly IQmSpecCharLinkRepository _qmSpecCharLinkRepository;
        private readonly ISamplePlanRepository _samplePlanRepository;
        private readonly ICharacteristicRepository _characteristicRepository;
        private readonly IProductionUnitRepository _productionUnitRepository;
        private readonly ISpcRuleRepository _spcRuleRepository;
        private readonly IQmSpecRuleLinkRepository _qmSpecRuleLinkRepository;

        public QmSpecService(IMapper mapper, IQmSpecRepository qmSpecRepository, IQmSpecCharLinkRepository qmSpecCharLinkRepository, ISamplePlanRepository samplePlanRepository,
            ICharacteristicRepository characteristicRepository, IProductionUnitRepository productionUnitRepository, ISpcRuleRepository spcRuleRepository, IQmSpecRuleLinkRepository qmSpecRuleLinkRepository)
        {
            if (mapper == null || qmSpecRepository == null || qmSpecCharLinkRepository == null || samplePlanRepository == null || characteristicRepository == null
                || productionUnitRepository == null)
            {
                throw new ArgumentNullException();
            }
            _mapper = mapper;
            _qmSpecRepository = qmSpecRepository;
            _qmSpecCharLinkRepository = qmSpecCharLinkRepository;
            _samplePlanRepository = samplePlanRepository;
            _characteristicRepository = characteristicRepository;
            _productionUnitRepository = productionUnitRepository;
            _spcRuleRepository = spcRuleRepository;
            _qmSpecRuleLinkRepository = qmSpecRuleLinkRepository;
        }

        /// <summary>
        /// 新增质量规格, 必须关联实体、采样计划和特性
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task AddQmSpecAsync(QmSpecAddingInput input)
        {
            // 拥有相同名字的质量规格的开始时间不能相同
            if (await _qmSpecRepository.ExistsAsync(x => x.Qm_Spec_Name == input.Qm_Spec_Name && x.Start_Eff_Local == input.Start_Eff_Local))
            {
                throw new CustomException(Messages.QmSpecWithSameStartTime);
            }

            // 拥有相同名字的质量规格的版本不能相同
            if (await _qmSpecRepository.ExistsAsync(x => x.Qm_Spec_Name == input.Qm_Spec_Name && x.Ver_Id == input.Ver_Id))
            {
                throw new CustomException(Messages.QmSpecWithSameVerId);
            }

            // 实体必须存在
            var productionUnit = _productionUnitRepository.Find(x => x.Id == input.Ent_Id);
            if (productionUnit == null)
            {
                throw new CustomException(Messages.ProductionUnitNotFound);
            }

            // 采样计划必须存在
            var samplePlan = _samplePlanRepository.Find(x => x.Plan_Id == input.Plan_Id);
            if (samplePlan == null)
            {
                throw new CustomException(Messages.SamplePlanNotFound);
            }

            // 特性必须存在
            foreach (var item in input.QmSpecChars)
            {
                var characteristic = _characteristicRepository.Find(x => x.Char_Id == item.CharacteristicId);
                if (characteristic == null)
                {
                    throw new CustomException(Messages.CharacteristicNotFound);
                }
            }

            if (input.SpcRuleIds.Count != input.SpcRuleIds.Distinct().Count())
            {
                throw new CustomException(Messages.SpcRuleDuplication);
            }
            if (input.SpcRuleIds.Count > 0)
            {
                var spcRules = await _spcRuleRepository.Query().Where(x => input.SpcRuleIds.Contains(x.Rule_Id)).ToListAsync();
                if (spcRules.Count != input.SpcRuleIds.Count)
                {
                    throw new CustomException(Messages.SpcRuleNotFound);
                }
            }

            // 生成质量规格
            var qmSpec = new QmSpec(input.Qm_Spec_Name, input?.Qm_Spec_Desc, input.Edit_Level, input.Ver_Id, input.Start_Eff_Local, input?.End_Eff_Local, input?.Ent_Id, input?.Plan_Id, input?.Num_Pts_Per_Page, input.Display_Seq, DateTime.Now);
            await _qmSpecRepository.AddAsync(qmSpec);
            int status = _qmSpecRepository.Save();
            if (status <= 0)
            {
                throw new CustomException(Messages.SavingFailure);
            }

            // 关联特性
            var qmSpecCharLinks = new HashSet<QmSpecCharLink>();
            foreach (var item in input.QmSpecChars)
            {
                var qmSpecCharLink = new QmSpecCharLink(qmSpec.Qm_Spec_Id, item.CharacteristicId, item.Target, item?.Usv, item?.Lsv, DateTime.Now);
                qmSpecCharLinks.Add(qmSpecCharLink);
            }

            if (qmSpecCharLinks.Count > 0)
            {
                await _qmSpecCharLinkRepository.AddRangeAsync(qmSpecCharLinks);
            }
            else // 若关联的特性不存在，则删除前面生成的质量规格
            {
                _qmSpecRepository.Delete(qmSpec);
                status = _qmSpecRepository.Save();
                if (status <= 0)
                {
                    throw new CustomException(Messages.SavingFailure);
                }
            }

            // 若质量规格关联了判异规则，则生成质量规格和判异规则的关联记录
            if (input.SpcRuleIds.Count > 0)
            {
                var links = new List<QmSpecRuleLink>();
                foreach (var item in input.SpcRuleIds)
                {
                    var link = new QmSpecRuleLink
                    {
                        Rule_Id = item,
                        Qm_Spec_Id = qmSpec.Qm_Spec_Id
                    };
                    links.Add(link);
                }
                await _qmSpecRuleLinkRepository.AddRangeAsync(links);
            }

            status = _qmSpecRepository.Save();
            if (status <= 0)
            {
                throw new CustomException(Messages.SavingFailure);
            }
        }

        /// <summary>
        /// 编辑质量规格
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task UpdateQmSpecAsync(QmSpecUpdatingInput input)
        {
            // 质量规格必须存在
            var qmSpec = _qmSpecRepository.Find(x => x.Qm_Spec_Id == input.Qm_Spec_Id);
            if (qmSpec == null)
            {
                throw new CustomException(Messages.QmSpecNotFound);
            }

            // 实体必须存在
            var productionUnit = _productionUnitRepository.Find(x => x.Id == input.Ent_Id);
            if (productionUnit == null)
            {
                throw new CustomException(Messages.ProductionUnitNotFound);
            }

            // 采样计划必须存在
            var samplePlan = _samplePlanRepository.Find(x => x.Plan_Id == input.Plan_Id);
            if (samplePlan == null)
            {
                throw new CustomException(Messages.SamplePlanNotFound);
            }

            if (input.SpcRuleIds.Count != input.SpcRuleIds.Distinct().Count())
            {
                throw new CustomException(Messages.SpcRuleDuplication);
            }
            if (input.SpcRuleIds.Count > 0)
            {
                var spcRules = await _spcRuleRepository.Query().Where(x => input.SpcRuleIds.Contains(x.Rule_Id)).ToListAsync();
                if (spcRules.Count != input.SpcRuleIds.Count)
                {
                    throw new CustomException(Messages.SpcRuleNotFound);
                }
            }

            _qmSpecCharLinkRepository.Delete(x => x.Qm_Spec_Id == input.Qm_Spec_Id);
            _qmSpecRuleLinkRepository.Delete(x => x.Qm_Spec_Id == input.Qm_Spec_Id);

            // 更新质量规格关联的特性
            var qmSpecCharLinks = new HashSet<QmSpecCharLink>();
            foreach (var item in input.QmSpecChars)
            {
                // 特性必须存在
                var characteristic = _characteristicRepository.Find(x => x.Char_Id == item.CharacteristicId);
                if (characteristic == null)
                {
                    throw new CustomException(Messages.CharacteristicNotFound);
                }
                var qmSpecCharLink = new QmSpecCharLink(input.Qm_Spec_Id, item.CharacteristicId, item.Target, item?.Usv, item?.Lsv, DateTime.Now);
                qmSpecCharLinks.Add(qmSpecCharLink);
            }

            if (qmSpecCharLinks.Count == 0)
            {
                throw new CustomException(Messages.QmSpecCharLinkNotFound);
            }

            qmSpec.Qm_Spec_Name = input.Qm_Spec_Name;
            qmSpec.Qm_Spec_Desc = input?.Qm_Spec_Desc;
            qmSpec.Edit_Level = input.Edit_Level;
            qmSpec.Ver_Id = input.Ver_Id;
            qmSpec.Start_Eff_Local = input.Start_Eff_Local;
            qmSpec.End_Eff_Local = input?.End_Eff_Local;
            qmSpec.Ent_Id = input?.Ent_Id;
            qmSpec.Plan_Id = input?.Plan_Id;
            qmSpec.Num_Pts_Per_Page = input?.Num_Pts_Per_Page;
            qmSpec.Display_Seq = input.Display_Seq;
            qmSpec.Last_Edit_At = DateTime.Now;
            _qmSpecRepository.Update(qmSpec);
            _qmSpecCharLinkRepository.Delete(x => x.Qm_Spec_Id == input.Qm_Spec_Id);
            await _qmSpecCharLinkRepository.AddRangeAsync(qmSpecCharLinks);

            if (input.SpcRuleIds.Count > 0)
            {
                var links = new List<QmSpecRuleLink>();
                foreach (var item in input.SpcRuleIds)
                {
                    var link = new QmSpecRuleLink
                    {
                        Rule_Id = item,
                        Qm_Spec_Id = qmSpec.Qm_Spec_Id
                    };
                    links.Add(link);
                }
                await _qmSpecRuleLinkRepository.AddRangeAsync(links);
            }

            int status = _qmSpecRepository.Save();
            if (status <= 0)
            {
                throw new CustomException(Messages.SavingFailure);
            }
        }

        /// <summary>
        /// 删除质量规格
        /// </summary>
        /// <param name="qmSpecIds"></param>
        /// <returns></returns>
        public async Task DeleteQmSpecAsync(int[] qmSpecIds)
        {
            Expression<Func<QmSpec, bool>> qmSpecFilter = x => qmSpecIds.Contains(x.Qm_Spec_Id);
            if (qmSpecFilter == null)
            {
                throw new CustomException(Messages.QmSpecNotFound);
            }

            Expression<Func<QmSpecCharLink, bool>> qmSpecCharLinkFilter = x => qmSpecIds.Contains(x.Qm_Spec_Id);
            if (qmSpecCharLinkFilter == null)
            {
                throw new CustomException(Messages.QmSpecCharLinkNotFound);
            }

            await _qmSpecRepository.DeleteAsync(qmSpecFilter);
            await _qmSpecCharLinkRepository.DeleteAsync(qmSpecCharLinkFilter);
            await _qmSpecRuleLinkRepository.DeleteAsync(x => qmSpecIds.Contains(x.Qm_Spec_Id));
            int status = _qmSpecRepository.Save();
            if (status <= 0)
            {
                throw new CustomException(Messages.SavingFailure);
            }
        }

        /// <summary>
        /// 根据质量规格id查询质量规格
        /// </summary>
        /// <param name="qmSpecId"></param>
        /// <returns></returns>
        public async Task<QmSpecResult> GetQmSpecByIdAsync(int qmSpecId)
        {
            // 质量规格必须存在
            var qmSpec = await _qmSpecRepository.Query().Where(x => x.Qm_Spec_Id == qmSpecId).FirstOrDefaultAsync();
            if (qmSpec == null)
            {
                throw new CustomException(Messages.QmSpecNotFound);
            }

            // 实体必须存在
            var productionUnit = await _productionUnitRepository.FindAsync(x => x.Id == qmSpec.Ent_Id);
            if (productionUnit == null)
            {
                throw new CustomException(Messages.ProductionUnitNotFound);
            }

            // 采样计划必须存在
            var samplePlan = await _samplePlanRepository.FindAsync(x => x.Plan_Id == qmSpec.Plan_Id);
            if (samplePlan == null)
            {
                throw new CustomException(Messages.SamplePlanNotFound);
            }

            // 质量规格和特性的关联记录必须存在
            var qmSpecCharLinks = await _qmSpecCharLinkRepository.Query().Where(x => x.Qm_Spec_Id == qmSpecId).ToListAsync();
            if (qmSpecCharLinks == null || qmSpecCharLinks.Count() == 0)
            {
                throw new CustomException(Messages.QmSpecCharLinkNotFound);
            }

            var qmSpecResult = _mapper.Map<QmSpecResult>(qmSpec);
            qmSpecResult.QmSpecChars = new List<QmSpecCharacteristicResult>();
            foreach (var qmSpecCharLink in qmSpecCharLinks)
            {
                // 关联的特性必须存在
                var characteristic = _characteristicRepository.Find(x => x.Char_Id == qmSpecCharLink.Char_Id);
                if (characteristic == null)
                {
                    throw new CustomException(Messages.CharacteristicNotFound);
                }

                QmSpecCharacteristicResult qmSpecCharacteristicResult = new QmSpecCharacteristicResult(characteristic.Char_Id, characteristic.Char_Name, characteristic?.Char_Desc,
                    qmSpecCharLink.Target, qmSpecCharLink.Usv, qmSpecCharLink.Lsv);
                qmSpecResult.QmSpecChars.Add(qmSpecCharacteristicResult);
            }

            qmSpecResult.ProductionUnitName = productionUnit.ProductionUnitName;
            qmSpecResult.ProductionUnitCode = productionUnit.ProductionUnitCode;
            qmSpecResult.Plan_Name = samplePlan.Plan_Name;
            qmSpecResult.Plan_Desc = samplePlan.Plan_Desc;
            qmSpecResult.Sample_Name = samplePlan.Sample_Name;

            var spcRuleLinks = await _qmSpecRuleLinkRepository.Query().Where(x => x.Qm_Spec_Id == qmSpecId).ToListAsync();
            var spcRuleIds = spcRuleLinks.Select(x => x.Rule_Id).ToList();
            if (spcRuleIds.Count > 0)
            {
                var spcRules = await _spcRuleRepository.Query().Where(x => spcRuleIds.Contains(x.Rule_Id)).ToListAsync();
                qmSpecResult.SpcRules= _mapper.Map<List<SpcRuleResult>>(spcRules);
            }
            return qmSpecResult;
        }

        /// <summary>
        /// 分页查询质量规格
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task<PagedItemsData<QmSpecResult>> GetQmSpecsAsync(QmSpecPagedInput input)
        {
            Expression<Func<QmSpec, bool>> filter = x => true;
            if (!string.IsNullOrEmpty(input.KeyWord))
            {
                filter = filter.And(x => x.Qm_Spec_Name.Contains(input.KeyWord) || x.Qm_Spec_Desc.Contains(input.KeyWord));
            }
            var qmSpecResult = _mapper.Map<PagedItemsData<QmSpecResult>>(await _qmSpecRepository.Query().Where(filter).OrderBy(x => x.Qm_Spec_Id).ToPagedAsync(input.PagedInput));
            if (qmSpecResult == null || qmSpecResult.Items.Count() == 0) // 没有查到符合条件的质量规格
            {
                return qmSpecResult;
            }

            var qmSpecIds = qmSpecResult.Items.Select(x => x.Qm_Spec_Id).ToList();
            var spcRuleLinks = await _qmSpecRuleLinkRepository.Query().Where(x => qmSpecIds.Contains(x.Qm_Spec_Id)).ToListAsync();
            var spcRuleIds=spcRuleLinks.Select(x => x.Rule_Id).Distinct().ToList();
            var spcRules = await _spcRuleRepository.Query().Where(x => spcRuleIds.Contains(x.Rule_Id)).ToListAsync();

            foreach (var item in qmSpecResult.Items)
            {
                var qmSpecCharLinks = await _qmSpecCharLinkRepository.Query().Where(x => x.Qm_Spec_Id == item.Qm_Spec_Id).ToListAsync();
                if (qmSpecCharLinks == null || qmSpecCharLinks.Count() == 0)
                {
                    throw new CustomException(Messages.QmSpecCharLinkNotFound);
                }

                item.QmSpecChars = new List<QmSpecCharacteristicResult>();
                foreach (var qmSpecCharLink in qmSpecCharLinks)
                {
                    var characteristic = _characteristicRepository.Find(x => x.Char_Id == qmSpecCharLink.Char_Id);
                    if (characteristic == null)
                    {
                        throw new CustomException(Messages.CharacteristicNotFound);
                    }
                    QmSpecCharacteristicResult qmSpecCharacteristicResult = new QmSpecCharacteristicResult(characteristic.Char_Id, characteristic.Char_Name, characteristic?.Char_Desc,
                        qmSpecCharLink.Target, qmSpecCharLink.Usv, qmSpecCharLink.Lsv);
                    item.QmSpecChars.Add(qmSpecCharacteristicResult);
                }

                var productionUnit = _productionUnitRepository.Find(x => x.Id == item.Ent_Id);
                if (productionUnit == null)
                {
                    throw new CustomException(Messages.ProductionUnitNotFound);
                }

                var samplePlan = _samplePlanRepository.Find(x => x.Plan_Id == item.Plan_Id);
                if (samplePlan == null)
                {
                    throw new CustomException(Messages.SamplePlanNotFound);
                }

                item.ProductionUnitCode = productionUnit.ProductionUnitCode;
                item.ProductionUnitName = productionUnit.ProductionUnitName;
                item.Plan_Name = samplePlan.Plan_Name;
                item.Plan_Desc = samplePlan.Plan_Desc;
                item.Sample_Name = samplePlan.Sample_Name;

                var currentSpcRuleLinks= spcRuleLinks.Where(x => x.Qm_Spec_Id == item.Qm_Spec_Id).ToList();
                var currentSpcRuleIds = currentSpcRuleLinks.Select(x => x.Rule_Id).ToList();
                var currentSpcRules = spcRules.Where(x => currentSpcRuleIds.Contains(x.Rule_Id)).ToList();
                item.SpcRules = _mapper.Map<List<SpcRuleResult>>(currentSpcRules);
            }
            return qmSpecResult;
        }
    }
}
